﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#ifndef __MYSQL_WRAPPER_INL__
#define __MYSQL_WRAPPER_INL__

#pragma once

#ifdef USE_MYSQL_API
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CMysqlWrapper
INLINE bool CMysqlWrapper::LibraryInit(void)
{
    return (mysql_library_init(0, nullptr, nullptr) == 0);
}

INLINE void CMysqlWrapper::LibraryExit(void)
{
    mysql_library_end();
}

INLINE bool CMysqlWrapper::ThreadInit(void)
{
    return (mysql_thread_init() == 0);
}

INLINE void CMysqlWrapper::ThreadExit(void)
{
    mysql_thread_end();
}

INLINE CMysqlWrapper::CMysqlWrapper(bool bThreads)
: m_pConnect(nullptr)
, m_pResult(nullptr)
, m_pLog(nullptr)
, m_nStatus(0)
, m_nTryCount(0)
, m_uErrorId(0)
, m_bThreads(bThreads)
{
}

INLINE CMysqlWrapper::~CMysqlWrapper(void)
{
    Disconnect();
}

INLINE bool CMysqlWrapper::Connect(CString& strHost, CString& strUser, CString& strPassWD, CString& strDB, UInt uPort, ULong ulFlag)
{
    return Connect(*strHost, *strUser, *strPassWD, *strDB, uPort, ulFlag);
}

INLINE bool CMysqlWrapper::Connect(PCXStr pszHost, PCXStr pszUser, PCXStr pszPassWD, PCXStr pszDB, UInt uPort, ULong ulFlag)
{
    Disconnect();
    m_pConnect = mysql_init(nullptr);
    if (IsConnect() == false)
    {
        DEV_ERROR(TF("CMysqlWrapper connect host=%s[%d] user=%s pass=%s db=%s with flag=%d error by out of memory"),
                  pszHost, uPort, pszUser, pszPassWD, pszDB, ulFlag);
        if (m_pLog != nullptr)
        {
            LOGV_ERROR(*m_pLog, TF("CMysqlWrapper connect host=%s[%d] user=%s pass=%s db=%s with flag=%d error by out of memory"),
                       pszHost, uPort, pszUser, pszPassWD, pszDB, ulFlag);
        }
        return false;
    }

    if (mysql_real_connect(m_pConnect, pszHost, pszUser, pszPassWD, pszDB, uPort, nullptr, ulFlag) != nullptr)
    {
        DEV_DUMP(TF("CMysqlWrapper connect host=%s[%d] user=%s pass=%s db=%s with flag=%d return okay"),
                 pszHost, uPort, pszUser, pszPassWD, pszDB, ulFlag);
        if (m_pLog != nullptr)
        {
            LOGV_DUMP(*m_pLog, TF("CMysqlWrapper connect host=%s[%d] user=%s pass=%s db=%s with flag=%d return okay"),
                      pszHost, uPort, pszUser, pszPassWD, pszDB, ulFlag);
        }
        my_bool bValue = TRUE;
        mysql_options(m_pConnect, MYSQL_OPT_RECONNECT, &bValue);
    }
    else
    {
        DEV_ERROR(TF("CMysqlWrapper connect host=%s[%d] user=%s pass=%s db=%s with flag=%d return error=[%d]%s"),
                  pszHost, uPort, pszUser, pszPassWD, pszDB, ulFlag, mysql_errno(m_pConnect), mysql_error(m_pConnect));
        if (m_pLog != nullptr)
        {
            LOGV_ERROR(*m_pLog, TF("CMysqlWrapper connect host=%s[%d] user=%s pass=%s db=%s with flag=%d return error=[%d]%s"),
                       pszHost, uPort, pszUser, pszPassWD, pszDB, ulFlag, mysql_errno(m_pConnect), mysql_error(m_pConnect));
        }
        Disconnect();
    }
    return IsConnect();
}

INLINE void CMysqlWrapper::Disconnect(void)
{
    FreeResult();
    if (m_pConnect != nullptr)
    {
        mysql_close(m_pConnect);
        m_pConnect = nullptr;
    }
    m_nTryCount = 0;
}

INLINE void CMysqlWrapper::SetFileLog(CFileLog& Log)
{
    m_pLog = &Log;
}

INLINE Int CMysqlWrapper::GetStatus(void)
{
    return m_nStatus;
}

INLINE void CMysqlWrapper::SetStatus(Int nStatus)
{
    m_nStatus = nStatus;
}

INLINE bool CMysqlWrapper::IsConnect(void)
{
    return (m_pConnect != nullptr);
}

INLINE bool CMysqlWrapper::IsThreads(void)
{
    return m_bThreads;
}

INLINE bool CMysqlWrapper::IsResult(void)
{
    return (m_pResult != nullptr);
}

INLINE bool CMysqlWrapper::Select(const CString& strQuery)
{
    return Select(*strQuery);
}

INLINE bool CMysqlWrapper::Select(PCXStr pszQuery)
{
    if (IsConnect())
    {
        FreeResult();
        if (mysql_query(m_pConnect, pszQuery) == 0)
        {
            m_pResult = mysql_store_result(m_pConnect);
            return (GetRowCount() > 0);
        }
        else
        {
            TryPing(TF("Select"), pszQuery);
        }
    }
    return false;
}

INLINE bool CMysqlWrapper::Check(const CString& strQuery)
{
    return Check(*strQuery);
}

INLINE bool CMysqlWrapper::Check(PCXStr pszQuery)
{
    if (IsConnect())
    {
        FreeResult();
        if (mysql_query(m_pConnect, pszQuery) == 0)
        {
            m_pResult = mysql_store_result(m_pConnect);

            bool bRet = (GetRowCount() > 0);
            FreeResult();
            return bRet;
        }
        else
        {
            TryPing(TF("Check"), pszQuery);
        }
    }
    return false;
}

INLINE bool CMysqlWrapper::Insert(const CString& strQuery)
{
    return Insert(*strQuery);
}

INLINE bool CMysqlWrapper::Insert(PCXStr pszQuery)
{
    if (IsConnect())
    {
        if (mysql_query(m_pConnect, pszQuery) == 0)
        {
            return true;
        }
        else
        {
            TryPing(TF("Insert"), pszQuery);
        }
    }
    return false;
}

INLINE bool CMysqlWrapper::Update(const CString& strQuery)
{
    return Update(*strQuery);
}

INLINE bool CMysqlWrapper::Update(PCXStr pszQuery)
{
    if (IsConnect())
    {
        if (mysql_query(m_pConnect, pszQuery) == 0)
        {
            return true;
        }
        else
        {
            TryPing(TF("Update"), pszQuery);
        }
    }
    return false;
}

INLINE bool CMysqlWrapper::Delete(const CString& strQuery)
{
    return Delete(*strQuery);
}

INLINE bool CMysqlWrapper::Delete(PCXStr pszQuery)
{
    if (IsConnect())
    {
        if (mysql_query(m_pConnect, pszQuery) == 0)
        {
            return true;
        }
        else
        {
            TryPing(TF("Delete"), pszQuery);
        }
    }
    return false;
}

INLINE bool CMysqlWrapper::NextResult(void)
{
    if (IsConnect())
    {
        FreeResult();
        Int nRet = mysql_next_result(m_pConnect);
        if (nRet == 0)
        {
            m_pResult = mysql_store_result(m_pConnect);
            return true;
        }
        else if (nRet > 0)
        {
            TryPing(TF("NextResult"), TF("NextResult"));
        }
    }
    return false;
}

INLINE void CMysqlWrapper::FreeResult(void)
{
    if (m_pResult != nullptr)
    {
        mysql_free_result(m_pResult);
        m_pResult = nullptr;
    }
    m_uErrorId = 0;
}

INLINE ULLong CMysqlWrapper::GetRowCount(void)
{
    if (m_pResult != nullptr)
    {
        return mysql_num_rows(m_pResult);
    }
    return 0;
}

INLINE MYSQL_ROW CMysqlWrapper::GetRow(void)
{
    if (m_pResult != nullptr)
    {
        return mysql_fetch_row(m_pResult);
    }
    return nullptr;
}

INLINE PULong CMysqlWrapper::GetRowEachColLength(void)
{
    if (m_pResult != nullptr)
    {
        return mysql_fetch_lengths(m_pResult);
    }
    return 0;
}

INLINE UInt CMysqlWrapper::GetFieldCount(void)
{
    if (IsConnect())
    {
        return mysql_field_count(m_pConnect);
    }
    return 0;
}

INLINE ULLong CMysqlWrapper::GetAffectedRowCount(void)
{
    if (IsConnect())
    {
        return mysql_affected_rows(m_pConnect);
    }
    return 0;
}

INLINE UInt CMysqlWrapper::GetColCount(void)
{
    if (m_pResult != nullptr)
    {
        return mysql_num_fields(m_pResult);
    }
    return 0;
}

INLINE MYSQL_FIELD* CMysqlWrapper::GetField(void)
{
    if (m_pResult != nullptr)
    {
        return mysql_fetch_field(m_pResult);
    }
    return nullptr;
}

INLINE MYSQL_FIELD* CMysqlWrapper::GetField(UInt uIndex)
{
    if (m_pResult != nullptr)
    {
        return mysql_fetch_field_direct(m_pResult, uIndex);
    }
    return nullptr;
}

INLINE MYSQL_FIELD* CMysqlWrapper::GetAllField(void)
{
    if (m_pResult != nullptr)
    {
        return mysql_fetch_fields(m_pResult);
    }
    return nullptr;
}

INLINE Int CMysqlWrapper::Ping(void)
{
    if (IsConnect())
    {
        return mysql_ping(m_pConnect);
    }
    return 0;
}

INLINE UInt CMysqlWrapper::GetErrorId(void)
{
    return m_uErrorId;
}

INLINE UInt CMysqlWrapper::TryPing(PCXStr pszMethod, PCXStr pszQuery)
{
    m_uErrorId = mysql_errno(m_pConnect);
    if ((m_uErrorId == CR_SERVER_GONE_ERROR) || (m_uErrorId == CR_SERVER_LOST))
    {
        DEV_WARN(TF("CMysqlWrapper %s[%s] return error=[%d]%s, try ping mysql server"), pszMethod, pszQuery, m_uErrorId, mysql_error(m_pConnect));
        if (m_pLog != nullptr)
        {
            LOGV_WARN(*m_pLog, TF("CMysqlWrapper %s[%s] return error=[%d]%s, try ping mysql server"), pszMethod, pszQuery, m_uErrorId, mysql_error(m_pConnect));
        }
        if (mysql_ping(m_pConnect) != 0)
        {
            ++m_nTryCount;
        }
        else
        {
            m_nTryCount = 0;
        }
        if (m_nTryCount >= MYSQL_TRY_MAX)
        {
            Disconnect();
        }
    }
    else
    {
        DEV_WARN(TF("CMysqlWrapper %s[%s] return error=[%d]%s"), pszMethod, pszQuery, m_uErrorId, mysql_error(m_pConnect));
        if (m_pLog != nullptr)
        {
            LOGV_WARN(*m_pLog, TF("CMysqlWrapper %s[%s] return error=[%d]%s"), pszMethod, pszQuery, m_uErrorId, mysql_error(m_pConnect));
        }
    }
    return m_uErrorId;
}

#endif // USE_MYSQL_API

#endif // __MYSQL_WRAPPER_INL__
